//
//  MoQue.swift
//  ios
//
//  Created by zhu xietong on 2020/4/9.
//  Copyright © 2020 zhu xietong. All rights reserved.
//

import Foundation
import UIKit

public enum MoTimeInterval{
    case interval(TimeInterval)
    case `default`
    case auto
}
public enum MoTaskStatus{
    case waiting
    case end
    case progress
    case tash
}
public protocol MoTaskProtocol:NSObjectProtocol{
    associatedtype T: MoTaskProtocol

    var ID:String?{get set}
    var changeStatus:(_:MoTaskStatus,_:T)->Void {get set}
    var status:MoTaskStatus  {get set}
    func start()
    
    func end()
    func tash()

}

//open class MoTask{
//    enum Status {
//        case waiting
//        case end
//        case progress
//        case tash
//    }
//
//    var ID:String? = nil
//    var changeStatus:(_:Status,_:MoTask)->Void = {_,_ in}
//    var status = Status.waiting{
//        didSet{
//            changeStatus(status,self)
//        }
//    }
//    open func start(){
//        status = .progress
//    }
//    open func end(){
//        status = .end
//    }
//    open func tash(){
//        status = .tash
//    }
//}

@objcMembers
open class MoQueue<Task:MoTaskProtocol>: NSObject {
    
    public typealias TaskObj = Task
    public enum Action {
        case start(task:TaskObj)
        case end(task:TaskObj)
    }

    public var tasks: [TaskObj] = []
    public var maxOperationCount = 3
    public var observation:(_:Action)->Void = {_ in }
    
    public var uiDelayJust:(Double,Double,Double) = (0.3,0.21,0.2)
    
    
    
    public func append(task:TaskObj) {
        if let tId = task.ID{
            for task_ in tasks {
                if let a_tId = task_.ID{
                    if tId == a_tId{
                        return//task 已存在
                    }
                }
            }
        }
        
        tasks.append(task)
        task.changeStatus = {[weak self]
            status,_task in
            guard let ws = self else {return}
            switch status {
            case .end:
                ws.remove(task: _task as! Task)
                ws.observation(.end(task: _task as! Task))
            case .waiting:
                break
            case .progress:
                ws.observation(.start(task: _task as! Task))
            case .tash:
                ws.tash(task: _task as! Task)
            }
        }
        __co_delay(delay: uiDelayJust.0) {[weak self] in
            self?.`continue`()
        }
    }
    
    public func clear() {
        for task in tasks {
            if task.status == .progress{
                task.end()
                continue
            }
            if task.status == .waiting{
                task.status = .end
                continue
            }
        
        }
    }
    
    public func reset(){
        for task in tasks {
            if task.status == .progress{
                task.tash()
                continue
            }
            if task.status == .end{
                task.status = .tash
                continue
            }
            if task.status == .waiting{
                task.status = .tash
                continue
            }
        }
    }
    
    public func tash(task:TaskObj) {
        var n:Int? = nil
       var i = 0
       for t in tasks {
           if t === task{
               n = i
           }
           i = i + 1
       }
       if let index = n{
           if index < tasks.count{
               tasks.remove(at: index)
               if tasks.count < 1{
                    notaskTip()
               }
           }
       }
    }
    
    public func remove(task:TaskObj) {
        if let _ = tasks.remove(obj: task){
            if tasks.count < 1{
                __co_delay(delay: uiDelayJust.1) {[weak self] in
                    guard let ws = self else{return}
                    if ws.tasks.count < 1{
                        ws.notaskTip()
                    }
                }
            }
        }
        __co_delay(delay: uiDelayJust.2) {[weak self] in
            self?.`continue`()
        }
    }
    public var notaskTip:()->Void = {}
    
    public func `continue`() {
        var i = 0

        for t in tasks {
       
            if (t.status == .waiting) && (i < maxOperationCount){
//                print("O))))------start")
                t.start()
            }
            i = i + 1
        }
    }
}
extension Array where Self.Element : NSObjectProtocol {
    public mutating func remove(obj:Self.Element) ->Int? {
        var n:Int? = nil
        var i = 0
        for t in self {
            if t === obj{
                n = i
            }
            i = i + 1
        }
        
        if let index = n{
            self.remove(at: index)
        }
        return n
    }
}

public func __co_delay(delay:Double, closure:@escaping ()->Void) {
    
    let delayTime = DispatchTime.now() + Double(Int64(delay * Double(NSEC_PER_SEC))) / Double(NSEC_PER_SEC)
    DispatchQueue.main.asyncAfter(deadline: delayTime) {
        closure()
    }
    
}

open class MoToast: UIView{
    static var _share:MoToast? = nil
    public static var share:MoToast{
        get{
            guard let share = _share else {
                let share = MoToast()
                _share = share
                return share
            }
            return share
        }
    }
 
    public func clear() {
        queue.clear()
    }
    public func reset() {
        queue.reset()
        tips = []
        tableView.reloadData()
    }
    
    open var backView:UIView?{
           get{
            if let v = container{
                return v
            }
            var topW = UIApplication.shared.keyWindow
            for win in UIApplication.shared.windows.reversed() {
                if let top = topW{
                    if top === win{
                        continue
                    }
                    if (win.windowLevel > top.windowLevel) && !win.isHidden{
                        topW = win
                    }
                }
            }
            
            return topW
        }
    }
       
    open override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
        guard let p = keyPath else {
            return
        }
        if p == "contentSize"{
            self.heightConstrain.constant = tableView.contentSize.height + 20
            tableView.setNeedsLayout()
            tableView.layoutIfNeeded()
            setNeedsLayout()
            layoutIfNeeded()
        }
    }
        
    public let tableView = UITableView(frame: .zero, style: .plain)
    var heightConstrain = NSLayoutConstraint()
    public init() {
        super.init(frame: .zero)
        addLayoutRules()
    }
    
    func addLayoutRules() {
        tableView.delegate = self
        tableView.dataSource = self
        tableView.register(ToastCell.self, forCellReuseIdentifier: "cell")
//        tableView.separatorInset = UIEdgeInsets(top: 0, left: -10000, bottom: 0, right: 0)
        tableView.layoutMargins = UIEdgeInsets.zero
        tableView.estimatedRowHeight = 60
        tableView.rowHeight = UITableView.automaticDimension
        tableView.estimatedSectionHeaderHeight = 0
        tableView.sectionHeaderHeight = UITableView.automaticDimension
        tableView.sectionFooterHeight = 0
        tableView.sectionHeaderHeight = 0
        tableView.translatesAutoresizingMaskIntoConstraints = false
        tableView.clipsToBounds = false
        tableView.isScrollEnabled = false
        tableView.addObserver(self, forKeyPath: "contentSize", options: .new, context: nil)
        tableView.separatorStyle = .none
        tableView.separatorColor = .clear
        tableView.backgroundColor = .clear
        let view = UIView()
        view.backgroundColor = UIColor.clear
        tableView.tableFooterView = view

        self.clipsToBounds = false
        self.translatesAutoresizingMaskIntoConstraints = false

 
        addSubview(tableView)
        addConstraint(NSLayoutConstraint(item: tableView, attribute: .top, relatedBy: .equal, toItem: self, attribute: .top, multiplier: 1, constant: 0))
        addConstraint(NSLayoutConstraint(item: tableView, attribute: .bottom, relatedBy: .equal, toItem: self, attribute: .bottom, multiplier: 1, constant: 0))
        addConstraint(NSLayoutConstraint(item: tableView, attribute: .left, relatedBy: .equal, toItem: self, attribute: .left, multiplier: 1, constant: 0))
        addConstraint(NSLayoutConstraint(item: tableView, attribute: .right, relatedBy: .equal, toItem: self, attribute: .right, multiplier: 1, constant: 0))
        heightConstrain = NSLayoutConstraint(item: tableView, attribute: .height, relatedBy: .lessThanOrEqual, toItem: nil, attribute: .height, multiplier: 1, constant: 10)
        heightConstrain.priority = .init(rawValue: 950)
        tableView.addConstraint(heightConstrain)
        
        queue.notaskTip = {[weak self] in
            __co_delay(delay: 0.28) {
                self?.removeFromWindow()
            }
        }
        
        queue.observation = {[weak self]
            action in
            guard let ws = self else {
                return
            }
            switch action {
            case .end(task: let task):
                ws.removeTip(tip: task )
            case .start(task: let task):
                ws.appendTip(tip: task )
            }
        }
    }
    open override var intrinsicContentSize: CGSize{
        return CGSize(width: MoToast.width, height: UIScreen.main.bounds.size.height)
    }
    

    
    public func appendTip(tip:Tip) {
        let n = tips.count
        tips.append(tip)
        tableView.insertRows(at: [IndexPath(row: n, section: 0)], with: .top)
    }
    
    func removeTip(tip:Tip) {
        var n:Int? = nil
        var i = 0
        for t in tips {
            if t === tip{
                n = i
            }
            i = i + 1
        }
        if let index = n{
            tips.remove(at: index)
            tableView.deleteRows(at: [IndexPath(row: index, section: 0)], with: .top)
        }
    }
    
    func removeFromWindow() {
        tableView.reloadData()
        if let share_ = MoToast._share{
            if share_ === self{
                MoToast._share = nil
            }
        }
//        self.removeConstraint(heightConstrain)
        self.superview?.removeConstraints(cons)
        self.removeFromSuperview()
    }
    
    var cons:[NSLayoutConstraint] = []
    static let width:CGFloat = UIScreen.main.bounds.width - 100

    func addToWindow() {
        guard let back = self.backView else {
            return
        }
        if let _ = self.superview{
            return
        }
        self.translatesAutoresizingMaskIntoConstraints = false
        back.addSubview(self)
        var t = NSLayoutConstraint(item: self, attribute: .top, relatedBy: .equal, toItem: back, attribute: .top, multiplier: 1, constant: 0)
        if #available(iOS 11.0, *) {
            t = NSLayoutConstraint(item: self, attribute: .top, relatedBy: .equal, toItem: back.safeAreaLayoutGuide, attribute: .top, multiplier: 1, constant: 0)
        } else {
            // Fallback on earlier versions
        }
        let x = NSLayoutConstraint(item: self, attribute: .centerX, relatedBy: .equal, toItem: back, attribute: .centerX, multiplier: 1, constant: 0)
        let w = NSLayoutConstraint(item: self, attribute: .width, relatedBy: .lessThanOrEqual, toItem: nil, attribute: .width, multiplier: 1, constant: MoToast.width)
        cons = [t,x,w]
        back.addConstraints(cons)
        
//        heightConstrain = NSLayoutConstraint(item: self, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .height, multiplier: 1, constant: 0)
//        heightConstrain.priority = .init(rawValue: 800)
//        self.removeConstraint(heightConstrain)
        
        
    }
    
    public enum Mode {
        case danger(String)
        case info(String)
        case customView(UIView)
        case success(String)
        case warning(String)
    }
    
    var queue = MoQueue<Tip>()
    
    public class Tip: NSObject,MoTaskProtocol {
        
        public var ID: String? = nil
        //    var ID:String? = nil
        //    var changeStatus:(_:Status,_:MoTask)->Void = {_,_ in}
        //        didSet{
         //            changeStatus(status,self)
         //        }       //    var status = Status.waiting{
 
        //    }
        
        public var changeStatus: (MoTaskStatus, Tip) -> Void = {_,_ in}{
            didSet{
                
            }
        }
        
//        public var changeStatus: (MoTaskStatus, Tip) -> Void = {_,_  in}
        
        public var status: MoTaskStatus = .waiting{
            didSet{
                changeStatus(status,self)
            }
        }
        
        
        public func tash() {
            status = .end
        }
        
        public var timer:Timer?
        public var waitingTime:TimeInterval = 4
        public var duration:MoTimeInterval = MoTimeInterval.default{
            didSet{
                switch duration {
                case .auto:
                    waitingTime = 4
                case .default:
                    waitingTime = 4
                case .interval(let t):
                    waitingTime = t
                }
            }
        }
        
        
        public func start() {
            status = .progress
            startTimer()
        }
        public func end() {
            status = .end
            stopTimer()
        }
        

        func startTimer() {
            self.timer = Timer(timeInterval: 1, target: self, selector: #selector(check), userInfo: nil, repeats: true)
            if let tm = self.timer{
                RunLoop.current.add(tm, forMode: RunLoop.Mode.common)
            }
        }
           
        func stopTimer() {
//            if let tm = timer{
//            }
            self.timer?.invalidate()
            self.timer = nil
        }
        @objc func check() {
            waitingTime = waitingTime - 1.0
            if waitingTime <= 0{
                end()
            }
        }
      
        var mode:Mode
        init(mode:Mode,time:MoTimeInterval) {
            self.duration = time
            self.mode = mode
            super.init()
//            __co_delay(delay: 8) {[weak self] in
//                self?.end()
//            }
        }
        deinit {
            stopTimer()
        }
    }
    
    public weak var container:UIView? = nil
    public init(container:UIView?) {
        super.init(frame: .zero)
        self.container = container
        addLayoutRules()
    }
    
    required public init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    public var tips:[Tip] = []
        
    public func `do`(mode:Mode,id:String?=nil,time:MoTimeInterval?=nil) {
        addToWindow()
        let tip = Tip(mode: mode, time: time ?? .default)
        tip.ID = id
        queue.append(task: tip)
    }
    
}
extension UIColor{

public static func dyColor(light:UIColor,dark:UIColor)->UIColor{
    if #available(iOS 13.0, *) {
        return UIColor { (tcollection) -> UIColor in
            switch tcollection.userInterfaceStyle {
            case .dark:
                return dark
            default:
                return light
            }
        }
    } else {
        return light
    }
    }
}


extension MoToast:UITableViewDelegate,UITableViewDataSource{
    public class Style{
        static var backgroundColor:UIColor = .dyColor(light: UIColor(red: 0.7, green: 0.7, blue: 0.7, alpha: 0.5), dark: UIColor(red: 0.8, green: 0.8, blue: 0.8, alpha: 0.8))
        static var foregroundColor:UIColor = .dyColor(light: UIColor(red: 0.1, green: 0.1, blue: 0.1, alpha: 1), dark: UIColor(red: 0.9, green: 0.9, blue: 0.9, alpha: 0.91))
        
    }
    
    open class ToastCell:TipCell{
        var tip:Tip? = nil{
            didSet{
                guard let tp = tip else {
                    return
                }
                switch tp.mode {
                case .info(let msg):
                    messageL.text = msg
                case .success(let msg):
                    messageL.text = msg
                case .warning(let msg):
                    messageL.text = msg
                case .danger(let msg):
                    messageL.text = msg
                default:
                    break
                }
            }
        }
        
        public override func scrollViewWillBeginDecelerating(_ scrollView: UIScrollView) {
            if scrollView.contentOffset.y > 8{
                tip?.end()
            }else{
                tip?.startTimer()
            }
        }
        func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
                tip?.stopTimer()
        }
    }
        
    public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return tips.count
    }
       
    public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "cell") as! ToastCell
        if indexPath.row < tips.count{
            cell.tip = tips[indexPath.row]
        }
        cell.separatorInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: .greatestFiniteMagnitude)
        return cell
    }
}

open class TipCell: UITableViewCell,UIScrollViewDelegate{
    public let titleL = UILabel()
    public let messageL = UILabel()
    public let scrollView = UIScrollView()
    public override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
        self.selectionStyle = .none
        
        let blurView = UIVisualEffectView(effect: UIBlurEffect(style: .prominent))
        let babel = UIView()
        let scontent = UIView()
        blurView.translatesAutoresizingMaskIntoConstraints = false
        messageL.translatesAutoresizingMaskIntoConstraints = false
        titleL.translatesAutoresizingMaskIntoConstraints = false

        babel.translatesAutoresizingMaskIntoConstraints = false
        scontent.translatesAutoresizingMaskIntoConstraints = false
                
        scrollView.translatesAutoresizingMaskIntoConstraints = false
        scrollView.showsHorizontalScrollIndicator = false
        scrollView.showsVerticalScrollIndicator = false
        scrollView.alwaysBounceVertical = true
        scrollView.alwaysBounceHorizontal = false
        scrollView.isDirectionalLockEnabled = true
        scrollView.isScrollEnabled = true
        if #available(iOS 11.0, *) {
            scrollView.contentInsetAdjustmentBehavior = .never
        } else {
        }
        
        scrollView.delegate = self
        
        scrollView.clipsToBounds = false
        scontent.clipsToBounds = false
        contentView.clipsToBounds = false
        backgroundColor = .clear
        contentView.backgroundColor = .clear
        self.clipsToBounds = false
        
        
        contentView.addSubview(scrollView)
        let sT = NSLayoutConstraint(item: scrollView, attribute: .top, relatedBy: .equal, toItem: contentView, attribute: .top, multiplier: 1, constant: 0)
        sT.priority = .init(rawValue: 900);
        contentView.addConstraint(sT)
        contentView.addConstraint(NSLayoutConstraint(item: scrollView, attribute: .bottom, relatedBy: .equal, toItem: contentView, attribute: .bottom, multiplier: 1, constant: 0))
        contentView.addConstraint(NSLayoutConstraint(item: scrollView, attribute: .left, relatedBy: .equal, toItem: contentView, attribute: .left, multiplier: 1, constant: 0))
        contentView.addConstraint(NSLayoutConstraint(item: scrollView, attribute: .right, relatedBy: .equal, toItem: contentView, attribute: .right, multiplier: 1, constant: 0))
        
//        contentView.addConstraint(NSLayoutConstraint(item: scrollView, attribute: .height, relatedBy: .equal, toItem: contentView, attribute: .height, multiplier: 1, constant: 1))
//        contentView.addConstraint(NSLayoutConstraint(item: scrollView, attribute: .width, relatedBy: .equal, toItem: contentView, attribute: .width, multiplier: 1, constant: 1))
        
        scrollView.addSubview(scontent)
        let sT1 = NSLayoutConstraint(item: scontent, attribute: .top, relatedBy: .equal, toItem: scrollView, attribute: .top, multiplier: 1, constant: 0)
        sT1.priority = .init(rawValue: 900)
        scrollView.addConstraint(sT1)
        scrollView.addConstraint(NSLayoutConstraint(item: scontent, attribute: .bottom, relatedBy: .equal, toItem: scrollView, attribute: .bottom, multiplier: 1, constant: 0))
        scrollView.addConstraint(NSLayoutConstraint(item: scontent, attribute: .left, relatedBy: .equal, toItem: scrollView, attribute: .left, multiplier: 1, constant: 0))
        scrollView.addConstraint(NSLayoutConstraint(item: scontent, attribute: .right, relatedBy: .equal, toItem: scrollView, attribute: .right, multiplier: 1, constant: 0))
        scrollView.addConstraint(NSLayoutConstraint(item: scontent, attribute: .height, relatedBy: .equal, toItem: scrollView, attribute: .height, multiplier: 1, constant: 0))
        scrollView.addConstraint(NSLayoutConstraint(item: scontent, attribute: .width, relatedBy: .equal, toItem: scrollView, attribute: .width, multiplier: 1, constant: 0))
//        scrollView.backgroundColor = .systemRed
        
        clipsToBounds = false
        contentView.clipsToBounds = false
        scontent.addSubview(babel)
        
//        let X = NSLayoutConstraint(item: babel, attribute: .centerX, relatedBy: .equal, toItem: scontent, attribute: .centerX, multiplier: 1, constant: 0)
//        X.priority = .init(rawValue: 1000)
        babel.backgroundColor = MoToast.Style.backgroundColor
//        scontent.addConstraint(X)
        scontent.addConstraint(NSLayoutConstraint(item: babel, attribute: .top, relatedBy: .equal, toItem: scontent, attribute: .top, multiplier: 1, constant: 2))
        scontent.addConstraint(NSLayoutConstraint(item: babel, attribute: .bottom, relatedBy: .equal, toItem: scontent, attribute: .bottom, multiplier: 1, constant:-2))
        
        let L = NSLayoutConstraint(item: babel, attribute: .left, relatedBy: .equal, toItem: scontent, attribute: .left, multiplier: 1, constant: 4)
        let R = NSLayoutConstraint(item: babel, attribute: .right, relatedBy: .equal, toItem: scontent, attribute: .right, multiplier: 1, constant: -4)
        scontent.addConstraint(L)
        scontent.addConstraint(R)
        babel.addSubview(blurView)
        babel.addConstraint(NSLayoutConstraint(item: blurView, attribute: .top, relatedBy: .equal, toItem: babel, attribute: .top, multiplier: 1, constant: 0))
        babel.addConstraint(NSLayoutConstraint(item: blurView, attribute: .bottom, relatedBy: .equal, toItem: babel, attribute: .bottom, multiplier: 1, constant: 0))
        babel.addConstraint(NSLayoutConstraint(item: blurView, attribute: .left, relatedBy: .equal, toItem: babel, attribute: .left, multiplier: 1, constant: 0))
        babel.addConstraint(NSLayoutConstraint(item: blurView, attribute: .right, relatedBy: .equal, toItem: babel, attribute: .right, multiplier: 1, constant: 0))
        
        
        
        babel.addSubview(titleL)
        babel.addConstraint(NSLayoutConstraint(item: titleL, attribute: .top, relatedBy: .equal, toItem: babel, attribute: .top, multiplier: 1, constant: 12))
        babel.addConstraint(NSLayoutConstraint(item: titleL, attribute: .left, relatedBy: .equal, toItem: babel, attribute: .left, multiplier: 1, constant: 16))
        babel.addConstraint(NSLayoutConstraint(item: titleL, attribute: .right, relatedBy: .equal, toItem: babel, attribute: .right, multiplier: 1, constant: -16))
        
        babel.addSubview(messageL)
        babel.addConstraint(NSLayoutConstraint(item: messageL, attribute: .top, relatedBy: .equal, toItem: titleL, attribute: .bottom, multiplier: 1, constant: 12))
        babel.addConstraint(NSLayoutConstraint(item: messageL, attribute: .bottom, relatedBy: .equal, toItem: babel, attribute: .bottom, multiplier: 1, constant: -12))
        babel.addConstraint(NSLayoutConstraint(item: messageL, attribute: .left, relatedBy: .equal, toItem: babel, attribute: .left, multiplier: 1, constant: 16))
        babel.addConstraint(NSLayoutConstraint(item: messageL, attribute: .right, relatedBy: .equal, toItem: babel, attribute: .right, multiplier: 1, constant: -16))
        
        
//        messageL.addConstraint(NSLayoutConstraint(item: messageL, attribute: .width, relatedBy: .greaterThanOrEqual, toItem: nil, attribute: .width, multiplier: 1, constant: MoToast.width - 24))
//
        babel.layer.cornerRadius = 8
        
        titleL.numberOfLines = 3
        titleL.textColor =  MoToast.Style.foregroundColor
        titleL.font = .boldSystemFont(ofSize: 12)
        titleL.text = "提示".loc
        titleL.textAlignment = .left
        
        messageL.numberOfLines = 3
        messageL.textColor =  MoToast.Style.foregroundColor
        messageL.textAlignment = .left

        babel.clipsToBounds = true
    }
    
    
    public func scrollViewWillBeginDecelerating(_ scrollView: UIScrollView) {
        
    }
    
    required public init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    

    
}


